#include "GameUtilities.h"

namespace GameUtilities
{

	bool WorldToScreen(VMatrix& W2SMatrix, SD_Tools::Vector2& ScreenSize, SD_Tools::Vector& World, SD_Tools::Vector2* Screen)
	{
		Screen->x = W2SMatrix.x1 * World.x  +  W2SMatrix.x2 * World.y  +  W2SMatrix.x3 * World.z + W2SMatrix.x4;
		Screen->y = W2SMatrix.y1 * World.x  +  W2SMatrix.y2 * World.y  +  W2SMatrix.y3 * World.z + W2SMatrix.y4;
		float w = W2SMatrix.w1 * World.x  +  W2SMatrix.w2 * World.y  +  W2SMatrix.w3 * World.z + W2SMatrix.w4;
		if (w < 0.001f)
		{
			return false;
		}

		float invw = 1.0f / w;
		Screen->x *= invw;
		Screen->y *= invw;
		float x = ScreenSize.x / 2.0f;
		float y = ScreenSize.y / 2.0f;

		x += 0.5f * Screen->x * ScreenSize.x + 0.5f;
		y -= 0.5f * Screen->y * ScreenSize.y + 0.5f;

		Screen->x = x;
		Screen->y = y;

		return true;
	}

	bool IsVisible(SD_Tools::Vector& start, SD_Tools::Vector& end, Entity* pLocal, Entity* pEnemy)
	{
		void* a;
		void* b;
		a = pLocal->GetBaseEntity();
		b = pEnemy->GetBaseEntity();
		if (!a || !b)
		{
			return false;
		}
		CTraceFilterSkipTwoEntities tracefilter(a, b);
		Ray_t Ray;
		Trace_t trace;
		Ray.Init(start,end);
		g_pInterfaces.g_pEngineTrace->TraceRay(Ray, MASK_DEFAULT, &tracefilter, &trace);
		if(trace.fraction > 0.97f)
		{
			return true;
		}
		return false;
	}

	SD_Tools::Angle CreateAimAngle(SD_Tools::Vector& Origin, SD_Tools::Vector& Target)
	{
		SD_Tools::Vector Delta;
		SD_Tools::Angle AimAngle;

		Delta = SD_Tools::MathHelper::SubVector(Origin, Target);

		float Hyp = sqrt((Delta.x * Delta.x) + (Delta.y * Delta.y));

		AimAngle.x = SD_Tools::MathHelper::RadToDegf(atanf(Delta.z / Hyp));
		AimAngle.y = SD_Tools::MathHelper::RadToDegf(atanf(Delta.y / Delta.x));

		if(Delta.x >= 0.0f)
		{
			AimAngle.y += 180.0f;
		}
		AimAngle.z = 0.0f;

		FixAngle(&AimAngle);

		return AimAngle;
	}

	bool HasGun()
	{
		Entity* pMe = g_pInterfaces.g_pEntList->GetClientEntity(g_pInterfaces.g_pEngine->GetLocalPlayer());
		if(!pMe)
			return false;

		CBaseCombatWeapon* pWeapon = GameUtilities::GetBaseCombatWeapon(pMe->GetBaseEntity());
		if(!pWeapon)
			return false;

		int iWeapon = pWeapon->GetWeaponId();
		if(iWeapon == WeaponIds::C4 || iWeapon == WeaponIds::Knife || iWeapon == WeaponIds::HEGrenade || iWeapon == WeaponIds::Flashbang || iWeapon == WeaponIds::SmokeGrenade || iWeapon == WeaponIds::Base)
			return false;

		return true;
	}

	bool HasPistol()
	{
		Entity* pMe = g_pInterfaces.g_pEntList->GetClientEntity(g_pInterfaces.g_pEngine->GetLocalPlayer());
		if(!pMe)
			return false;

		CBaseCombatWeapon* pWeapon = GameUtilities::GetBaseCombatWeapon(pMe->GetBaseEntity());
		if(!pWeapon)
			return false;

		int iWeapon = pWeapon->GetWeaponId();
		if(iWeapon == WeaponIds::Glock || iWeapon == WeaponIds::USP || iWeapon == WeaponIds::FiveSeven || iWeapon == WeaponIds::Elite || iWeapon == WeaponIds::Deagle || iWeapon == WeaponIds::P228 || iWeapon == WeaponIds::AWP || iWeapon == WeaponIds::Scout)
			return true;

		return false;
	}

	bool HasKnife()
	{
		Entity* pMe = g_pInterfaces.g_pEntList->GetClientEntity(g_pInterfaces.g_pEngine->GetLocalPlayer());
		if(!pMe)
			return false;

		CBaseCombatWeapon* pWeapon = GameUtilities::GetBaseCombatWeapon(pMe->GetBaseEntity());
		if(!pWeapon)
			return false;

		int iWeapon = pWeapon->GetWeaponId();
		if(iWeapon == WeaponIds::Knife)
			return true;

		return false;
	}

	void FixAngle( SD_Tools::Angle* ToFixAngle )
	{
		ToFixAngle->Pitch = SD_Tools::MathHelper::ToRange(ToFixAngle->Pitch, -180.0f, 180.0f);
		ToFixAngle->Pitch = SD_Tools::MathHelper::FixRange(ToFixAngle->Pitch, -89.0f, 89.0f);
		ToFixAngle->Yaw = SD_Tools::MathHelper::ToRange(ToFixAngle->Yaw, -180.0f, 180.0f);
	}

	void AngleNormalize(SD_Tools::Vector &vIn, SD_Tools::Vector &vOut)
	{
		float flLen = vIn.Magnitude();
		if(flLen == 0)
		{
			vOut.x = 0;
			vOut.y = 0;
			vOut.z = 1;
			return;
		}
		flLen = 1 / flLen;
		vOut.x = vIn.x * flLen;
		vOut.y = vIn.y * flLen;
		vOut.z = vIn.z * flLen;
	}

	CBaseCombatWeapon* GetBaseCombatWeapon(void* pEntity)
	{
		CBaseHandle hActiveWeapon = *reinterpret_cast<CBaseHandle*>((DWORD)pEntity + Offsets::m_hActiveWeapon); 
		return (CBaseCombatWeapon*)(g_pInterfaces.g_pEntList->GetClientEntityFromHandle(hActiveWeapon)); 
	}

	void Predict(Entity* ent, SD_Tools::Vector& InOut, float flFactor)
	{
		SD_Tools::Vector Velocity;
		ent->GetVelocity(Velocity);
		InOut.x += (Velocity.x * g_pInterfaces.g_pGlobals->interval_per_tick * g_pInterfaces.g_pGlobals->interpolation_amount * flFactor);
		InOut.y += (Velocity.y * g_pInterfaces.g_pGlobals->interval_per_tick * g_pInterfaces.g_pGlobals->interpolation_amount * flFactor);
		InOut.z += (Velocity.z * g_pInterfaces.g_pGlobals->interval_per_tick * g_pInterfaces.g_pGlobals->interpolation_amount * flFactor);
	}

	char GetWeaponChar( int Id )
	{
		const char WeaponChars[30] = {'2', 'y', 'c', 'n', 'h', 'B', 'C', 'l', 'e', 'p', 's', 'u', 'q', 'o', 'v', 't', 'a', 'r', 'x', 'z', 'k', 'w', 'd', 'i', 'g', 'f', 'A', 'b', 'j', 'm'};
		if (Id < 30)
		{
			return WeaponChars[Id];
		}
		return WeaponChars[0];
	}

	float GetWeaponRange(int Id)
	{
		float WeaponRanges[30] = {4096.0f, 4096.0f, 8192.0f, 0.0f, 3000.0f, 0.0f, 4096.0f, 8192.0f, 0.0f, 4096.0f, 4096.0f, 4096.0f, 8192.0f, 8192.0f, 8192.0f, 4096.0f, 8192.0f, 4096.0f, 8192.0f, 3000.0f, 8192.0f, 4096.0f, 8192.0f, 0.0f, 4096.0f, 8192.0f, 8192.0f, 4096.0f, 4096.0f};
		if (Id < 30)
		{
			return WeaponRanges[Id];
		}
		return 0.0f;
	}

	float GetAngleBetween( SD_Tools::Vector& a, SD_Tools::Vector& b )
	{
		return SD_Tools::MathHelper::RadToDegf(acosf(SD_Tools::MathHelper::DotVector(a, b) / (a.Magnitude()*b.Magnitude())));
	}

	float GetMaxRange( SD_Tools::Vector& Origin, SD_Tools::Vector& Target, float Dist /*= 5.0f*/ )
	{
		SD_Tools::Vector NormalVec = SD_Tools::MathHelper::SubVector(Origin, Target);
		float TempFloat = 1/NormalVec.Magnitude();
		NormalVec.Multiply(TempFloat);

		float PlaneD = NormalVec.x * Target.x + NormalVec.y * Target.y + NormalVec.z * Target.z;

		SD_Tools::Vector TempVec1;
		SD_Tools::Vector TempVec2;
		SD_Tools::Vector TempVec3;

		if (NormalVec.y != 0.0f)
		{
			TempVec1.x = 0.0f;
			TempVec1.y = PlaneD/NormalVec.y;
			TempVec1.z = 0.0f;
		}
		if (NormalVec.x != 0.0f)
		{
			TempVec2.x = PlaneD/NormalVec.x;
			TempVec2.y = 0.0f;
			TempVec2.z = 0.0f;
		}
		if (NormalVec.z != 0.0f)
		{
			TempVec3.x = 0.0f;
			TempVec3.y = 0.0f;
			TempVec3.z = PlaneD/NormalVec.z;
		}
		
		SD_Tools::Vector RangeVec = (TempVec1.MagnitudeSqr() > TempVec2.MagnitudeSqr()) ? TempVec1 : TempVec2;
		RangeVec = (RangeVec.MagnitudeSqr() > TempVec3.MagnitudeSqr()) ? RangeVec : TempVec3;

		TempFloat = 1/RangeVec.Magnitude();
		RangeVec.Multiply(TempFloat);
		RangeVec.Multiply(Dist); // Size
		RangeVec.Add(Target);

		TempVec1 = SD_Tools::MathHelper::SubVector(Origin, Target);
		TempVec2 = SD_Tools::MathHelper::SubVector(Origin, RangeVec);

		return GameUtilities::GetAngleBetween(TempVec1, TempVec2);
	}

	

}